Skip to content

[#268] Writer dashboard genre prompt#283

Merged
realproject7 merged 2 commits intomainfrom
task/268-writer-genre-prompt
Mar 18, 2026
Merged

[#268] Writer dashboard genre prompt#283
realproject7 merged 2 commits intomainfrom
task/268-writer-genre-prompt

Conversation

@realproject7
Copy link
Copy Markdown
Owner

Summary

  • New PATCH /api/storyline/[storylineId]/metadata endpoint: validates caller is the storyline writer, accepts { genre?, language?, address }, updates Supabase
  • GenrePrompt inline component on writer dashboard: appears for storylines with genre=NULL, uses same genre/language lists from [feat] Add genre and language metadata to storylines with discovery filters #265, disappears after save
  • StoryCard shows "Uncategorized" (muted, italic) when genre is null instead of hiding the badge

Test plan

  • Writer dashboard shows genre prompt for storylines with null genre
  • Selecting a genre and clicking Save updates the storyline
  • After save, prompt disappears and genre badge shows normally
  • API rejects requests from non-writer addresses (403)
  • API rejects invalid genre values (400)
  • StoryCard on discover page shows "Uncategorized" for null-genre stories
  • npm run typecheck passes
  • npm run lint passes (no new warnings)

Fixes #268

🤖 Generated with Claude Code

- PATCH /api/storyline/[id]/metadata endpoint validates writer ownership
  and updates genre/language
- GenrePrompt inline component on writer dashboard for NULL-genre stories
- StoryCard shows "Uncategorized" fallback when genre is null

Fixes #268

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@project7-interns project7-interns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

T2b Review — REQUEST CHANGES

BLOCKING: route.ts — No real ownership verification

The PATCH endpoint accepts address from the request body and compares it against the writer_address in the DB. But there is no proof the caller actually owns that address — no wallet signature, no session auth. Anyone who knows a writer's address and storyline ID can spoof the request and change their genre/language.

This is the equivalent of an auth endpoint that takes { userId: 123 } and trusts it.

Fix options (pick one):

  1. Require a signed message (personal_sign or EIP-712) and recover the signer address server-side
  2. If there's existing session/auth middleware in this app that ties the request to a verified wallet, use that instead of the body address field

If this codebase already has other endpoints that use the same address-from-body pattern without signature verification, this is a pre-existing issue — note it but don't block on it here. But if other endpoints verify ownership differently, this one should too.

Non-blocking

  • StoryCard.tsx: "Uncategorized" badge is now always rendered even for categorized stories (the conditional was removed). This means every story card has a genre badge — fine UX-wise, just confirming that's intentional.
  • GenrePrompt: Clean implementation, proper query invalidation.

Copy link
Copy Markdown
Collaborator

@project7-interns project7-interns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: REQUEST CHANGES

Summary

The UI changes are scoped correctly, but the new metadata endpoint does not authenticate the caller. As written, anyone can update a storyline by spoofing the writer address in the request body.

Findings

  • [high] PATCH /api/storyline/[storylineId]/metadata authorizes based only on a user-supplied address field, which is trivially forgeable.
    • File: src/app/api/storyline/[storylineId]/metadata/route.ts:33
    • Suggestion: Do not trust address from the JSON body as proof of wallet ownership. The current check only verifies storyline.writer_address === body.address, so any client can send the writer's public address and overwrite genre/language for that storyline. This needs a real authenticated identity source the server can verify, or the write must stay client-mediated through an authenticated backend path.

Decision

Requesting changes because this is an authorization bypass on a write endpoint.

Server verifies a signed message binding storylineId + genre + language
before allowing updates. Same pattern as /api/ratings. Client signs
via useSignMessage before sending the PATCH request.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@project7-interns project7-interns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

T2b Re-review — APPROVED

Auth fix is correct:

  • Server constructs expected message (Update storyline {id} metadata genre:{g} language:{l}) and compares against signed message — prevents replay and cross-action attacks
  • publicClient.verifyMessage supports both EOA and EIP-1271 contract wallets
  • Follows existing /api/ratings signature pattern
  • Client uses useSignMessage hook, signs before fetch

Rest of the PR (GenrePrompt, StoryCard fallback) is clean. Ship it.

Copy link
Copy Markdown
Collaborator

@project7-interns project7-interns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE

Summary

The metadata update endpoint now requires a signed message that is verified server-side and matched to the storyline writer before updating Supabase. The dashboard prompt and uncategorized fallback are aligned with issue #268.

Findings

  • No blocking findings from this review pass.

Decision

Approving from my side. The authorization bypass is addressed; GitHub checks were still pending at the time of review.

@realproject7 realproject7 merged commit 413f20b into main Mar 18, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feat] Writer dashboard prompt to set missing genre on uncategorized storylines

2 participants